package com.github.cyf.common.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * <p>
 * 自定义监听器
 * 不能被 Spring 管理，因为属性 list 是不能共享的，所以每次读取 Excel 都要使用 new 关键字，防止多个线程读取造成数据混乱。
 * 不能使用 @Autowired 注解注入，同样如果需要在监听器里面用到 Spring 管理的类，可以通过监听器的构造方法传进去。
 * </p>
 * 仅实现数据的读取
 */
@Data
public class DefaultDataListener<T> implements ReadListener<T> {

    private static final Logger logger = LoggerFactory.getLogger(DefaultDataListener.class);

    private List<T> list = new ArrayList<>();

    /**
     * 错误信息，key 为行，value 为错误信息
     */
    protected Map<Integer, String> errorMap = new TreeMap<>(Integer::compareTo);

    public boolean hasError() {
        return !errorMap.isEmpty();
    }

    public List<String> getErrorList() {
        return new ArrayList<>(errorMap.values());
    }

    public List<T> getList() {
        return list;
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        list.add(data);
    }

    /**
     * 对于异常的操作
     * 抛出异常则停止读取，如果这里不抛出异常则继续读取下一行。
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        if (exception instanceof ExcelDataConvertException) {
            String error;
            ExcelDataConvertException convertException = (ExcelDataConvertException) exception;

            int row = convertException.getRowIndex() + 1;
            int column = convertException.getColumnIndex() + 1;
            CellData<?> cellData = convertException.getCellData();

            // 判断出错原因
            Class<?> type = convertException.getExcelContentProperty().getField().getType();
            if (type.equals(Double.class)) {
                error = String.format("第%d行%d列数据【%s】有误，应该为小数类型.", row, column, cellData.getStringValue());
            } else if (type.equals(Integer.class) || type.equals(Long.class)) {
                error = String.format("第%d行%d列数据【%s】有误，应该为整数类型.", row, column, cellData.getStringValue());
            } else {
                error = String.format("第%d行%d列数据【%s】有误.", row, column, cellData.getStringValue());
            }
            String s = errorMap.get(row);
            if (s == null) {
                errorMap.put(row, error);
                return;
            }
            errorMap.put(row, s + error);
        }
    }

    /**
     * 读取完成后调用的方法
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        logger.info("读取完毕");
    }
}